{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Accessing C Struct Data\n",
    "\n",
    "This notebook illustrates the use of `@cfunc` to connect to data defined in C."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Via CFFI\n",
    "\n",
    "Numba can map simple C structure types (i.e. with scalar members only) into NumPy structured `dtype`s.\n",
    "\n",
    "Let's start with the following C declarations:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from cffi import FFI\n",
    "\n",
    "src = \"\"\"\n",
    "\n",
    "/* Define the C struct */\n",
    "typedef struct my_struct {\n",
    "    int    i1;\n",
    "    float  f2;\n",
    "    double d3;\n",
    "    float  af4[7];\n",
    "} my_struct;\n",
    "\n",
    "/* Define a callback function */\n",
    "typedef double (*my_func)(my_struct*, size_t);\n",
    "\"\"\"\n",
    "\n",
    "\n",
    "ffi = FFI()\n",
    "ffi.cdef(src)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can create `my_struct` data by doing:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Make a array of 3 my_struct\n",
    "mydata = ffi.new('my_struct[3]')\n",
    "ptr = ffi.cast('my_struct*', mydata)\n",
    "for i in range(3):\n",
    "    ptr[i].i1 = 123 + i\n",
    "    ptr[i].f2 = 231 + i\n",
    "    ptr[i].d3 = 321 + i\n",
    "    for j in range(7):\n",
    "        ptr[i].af4[j] = i * 10 + j"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Using `numba.cffi_support.map_type` we can convert the `cffi` type into a Numba `Record` type."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Record(i1[type=int32;offset=0],f2[type=float32;offset=4],d3[type=float64;offset=8],af4[type=nestedarray(float32, (7,));offset=16];48;True)"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from numba import cffi_support\n",
    "\n",
    "cffi_support.map_type(ffi.typeof('my_struct'), use_record_dtype=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The function type can be mapped in a signature:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Record(i1[type=int32;offset=0],f2[type=float32;offset=4],d3[type=float64;offset=8],af4[type=nestedarray(float32, (7,));offset=16];48;True)*, uint64) -> float64"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sig = cffi_support.map_type(ffi.typeof('my_func'), use_record_dtype=True)\n",
    "sig"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "and `@cfunc` can take that signature directly:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "from numba import cfunc, carray\n",
    "\n",
    "@cfunc(sig)\n",
    "def foo(ptr, n):\n",
    "    base = carray(ptr, n)  # view pointer as an array of my_struct\n",
    "    tmp = 0\n",
    "    for i in range(n):\n",
    "        tmp += base[i].i1 * base[i].f2 / base[i].d3 + base[i].af4.sum()\n",
    "    return tmp"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Testing the cfunc via the `.ctypes` callable:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "address of data: 0x7fb70a611b30\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "541.025912236192"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "addr = int(ffi.cast('size_t', ptr))\n",
    "print(\"address of data:\", hex(addr))\n",
    "result = foo.ctypes(addr, 3)\n",
    "result"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Manually creating a Numba `Record` type"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Sometimes it is useful to create a `numba.types.Record` type directly.  The easiest way is to use the `Record.make_c_struct()` method. Using this method, the field offsets are calculated from the natural size and alignment of prior fields.\n",
    "\n",
    "In the example below, we will manually create the *my_struct* structure from above."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Record(i1[type=int32;offset=0],f2[type=float32;offset=4],d3[type=float64;offset=8],af4[type=nestedarray(float32, (7,));offset=16];48;True)"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from numba import types\n",
    "\n",
    "\n",
    "my_struct = types.Record.make_c_struct([\n",
    "    # Provides a sequence of 2-tuples i.e. (name:str, type:Type)\n",
    "    ('i1', types.int32),\n",
    "    ('f2', types.float32),\n",
    "    ('d3', types.float64),\n",
    "    ('af4', types.NestedArray(dtype=types.float32, shape=(7,)))\n",
    "])\n",
    "\n",
    "my_struct"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here's another example to demonstrate the offset calculation:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Record(i1[type=int32;offset=0],pad0[type=int8;offset=4],f2[type=float32;offset=8],pad1[type=int8;offset=12],d3[type=float64;offset=16];24;True)"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "padded = types.Record.make_c_struct([\n",
    "    ('i1', types.int32),\n",
    "    ('pad0', types.int8),    # padding bytes to move the offsets\n",
    "    ('f2', types.float32),\n",
    "    ('pad1', types.int8),    # padding bytes to move the offsets\n",
    "    ('d3', types.float64),\n",
    "])\n",
    "\n",
    "padded"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice how the byte at `pad0` and `pad1` moves the offset of `f2` and `d3`. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A function signature can also be created manually:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "signature: (Record(i1[type=int32;offset=0],f2[type=float32;offset=4],d3[type=float64;offset=8],af4[type=nestedarray(float32, (7,));offset=16];48;True)*, uint64) -> float64\n",
      "signature matches: True\n"
     ]
    }
   ],
   "source": [
    "new_sig = types.float64(types.CPointer(my_struct), types.uintp)\n",
    "print('signature:', new_sig)\n",
    "# Our new signature matches the previous auto-generated one.\n",
    "print('signature matches:', new_sig == sig)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
